package com.pap.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 *  作用： 监听 nacos 配置中心的数据变化，动态调整网关限流处理逻辑；
 *  nacos 自定义命名空间；在对应的配置列表信息中添加配置项(数据格式如下所示)。
 *
 * [{
 * 	"resource": "pap-uua-get",
 * 	"resourceMode": 0,
 * 	"grade": 0,
 * 	"count": 0,
 * 	"intervalSec": 1,
 * 	"controlBehavior": 0,
 * 	"burst": 0,
 * 	"maxQueueingTimeoutMs": 0,
 * 	"paramItem": {
 * 		"parseStrategy": 2,
 * 		"fieldName": "papToken",
 * 		"pattern": "pap",
 * 		"matchStrategy": 0
 *        }
 * }]
 */
@Component
public class SentinelGatewayPropertiesByNacos {

    private static Logger logger  =  LoggerFactory.getLogger(SentinelGatewayPropertiesByNacos.class);

    public SentinelGatewayPropertiesByNacos() {
        dynamicRouteByNacosListener("192.168.7.39", "c449bad0-a2a2-4055-90bd-a846ec9e6202",
                "com.pap.gateway.config.rule", "GATEWAY_GROUP");
//        Set<GatewayFlowRule> rules = new HashSet<>();
//        rules.add(new GatewayFlowRule()
//                // 资源名称，可以是网关中的 route 名称或者用户自定义的 API 分组名称。
//                .setResource("pap-uua-get")
//                // 规则是针对 API Gateway 的 route（RESOURCE_MODE_ROUTE_ID）还是用户在 Sentinel 中定义的 API 分组（RESOURCE_MODE_CUSTOM_API_NAME），默认是 route。
//                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
//                // grade: 限流阈值类型（QPS 或并发线程数）
//                // 流量控制主要有两种统计类型，一种是统计并发线程数，另外一种则是统计 QPS。类型由 FlowRule 的 grade 字段来定义。
//                // 其中，0 代表根据并发数量来限流，1 代表根据 QPS 来进行流量控制。其中线程数、QPS 值，都是由 StatisticSlot 实时统计获取的。
//                .setGrade(RuleConstant.FLOW_GRADE_THREAD)
//                // 限流阈值
//                .setCount(0)
//                // 统计时间窗口，单位是秒，默认是 1 秒。
//                .setIntervalSec(1)
//                // 流量整形的控制效果，同限流规则的 controlBehavior 字段，目前支持快速失败和匀速排队两种模式，默认是快速失败。
//                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
//                // 应对突发请求时额外允许的请求数目。
//                .setBurst(0)
//                // 匀速排队模式下的最长排队时间，单位是毫秒，仅在匀速排队模式下生效。
//                .setMaxQueueingTimeoutMs(0)
//                // 参数限流配置。若不提供，则代表不针对参数进行限流，该网关规则将会被转换成普通流控规则；否则会转换成热点规则。
//                .setParamItem(new GatewayParamFlowItem()
//                        // 从请求中提取参数的策略，目前支持提取来源 IP（PARAM_PARSE_STRATEGY_CLIENT_IP）、Host（PARAM_PARSE_STRATEGY_HOST）、
//                        // 任意 Header（PARAM_PARSE_STRATEGY_HEADER）和任意 URL 参数（PARAM_PARSE_STRATEGY_URL_PARAM）四种模式。
//                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
//                        // 若提取策略选择 Header 模式或 URL 参数模式，则需要指定对应的 header 名称或 URL 参数名称。
//                        .setFieldName("papToken")
//                        // 参数值的匹配模式，只有匹配该模式的请求属性值会纳入统计和流控；若为空则统计该请求属性的所有值。（1.6.2 版本开始支持）
//                        .setPattern("pap")
//                        // 参数值的匹配策略，目前支持精确匹配（PARAM_MATCH_STRATEGY_EXACT）、子串匹配（PARAM_MATCH_STRATEGY_CONTAINS）和正则匹配（PARAM_MATCH_STRATEGY_REGEX）。（1.6.2 版本开始支持）
//                        .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
//                )
//
//        );
//        // 用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则，
//        // 或通过 GatewayRuleManager.register2Property(property) 注册动态规则源动态推送（推荐方式）。
//        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 监听Nacos Server下发的动态路由配置
     * @param dataId
     * @param groupId
     */
    public void dynamicRouteByNacosListener (String serverAddr, String nameSpace, String dataId, String groupId){
        try {
            Properties gatewayProperties = new Properties();
            gatewayProperties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
            gatewayProperties.put(PropertyKeyConst.NAMESPACE, nameSpace);
            ConfigService configService = NacosFactory.createConfigService(gatewayProperties);
            configService.addListener(dataId, groupId, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    try {
                        // TODO 注意这里根据监听到的数据变化，进行 网关流控 控制
                        ObjectMapper mapper = new ObjectMapper();
                        Set<GatewayFlowRule> rulesSet = mapper.readValue(configInfo, new TypeReference<Set<GatewayFlowRule>>() {});
                        DynamicSentinelProperty<Set<GatewayFlowRule>> dynamicSentinelProperty = new DynamicSentinelProperty<>(rulesSet);
                        GatewayRuleManager.register2Property(dynamicSentinelProperty);
                    } catch (IOException e) {
                        logger.error("SentinelGatewayPropertiesByNacos.dynamicRouteByNacosListener.网关流控下发更新: [{}]", e.getMessage());
                    }
                }
                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            logger.error("SentinelGatewayPropertiesByNacos.dynamicRouteByNacosListener.网关流控下发更新: [{}]", e.getMessage());
        }
    }
}
